[WebGPU-XR][Phase 2] WebGPU projection layer + render target provider (#18640)#18655
[WebGPU-XR][Phase 2] WebGPU projection layer + render target provider (#18640)#18655RaananW wants to merge 4 commits into
Conversation
Add @internal, un-barrelled WebGPU render target texture providers that mirror the WebGL layer providers but extend the API-agnostic base WebXRLayerRenderTargetTextureProvider. They wrap the per-view XRGPUSubImage color/depth GPUTextures via WebGPUEngine.wrapWebGPUTexture, set the correct Babylon depth format on the wrapped depth texture, repoint per-frame via updateWrappedWebGPUTexture (rebuilding only on size change), and build RTTs via _createRenderTargetTextureInternal. Includes unit tests. Inert until WebXRLayers wiring is activated. Part of Phase 2 (#18640), epic #18635. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Branch WebXRLayers attach()/isCompatible() on engine.isWebGPU to create an XRGPUBinding projection layer (via the Phase 1 graphics binding) and instantiate the WebGPU projection layer wrapper. The WebGL2 XR path is unchanged; the new branch is only taken when isWebGPU and an XRGPUBinding is present. Part of Phase 2 (#18640), epic #18635. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). |
|
Snapshot stored with reference name: Test environment: To test a playground add it to the URL, for example: https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/18655/merge/index.html#WGZLGJ#4600 Links to test your changes to core in the published versions of the Babylon tools (does not contain changes you made to the tools themselves): https://playground.babylonjs.com/?snapshot=refs/pull/18655/merge To test the snapshot in the playground with a playground ID add it after the snapshot query string: https://playground.babylonjs.com/?snapshot=refs/pull/18655/merge#BCU1XR#0 If you made changes to the sandbox or playground in this PR, additional comments will be generated soon containing links to the dev versions of those tools. |
|
WebGL2 visualization test reporter: |
|
Visualization tests for WebGPU |
⚡ Performance Test Results🟢 All performance tests passed — no regressions detected. |
🟢 Memory Leak Test Results4 passed, 0 leaked out of 4 scenarios 🟢 All memory leak tests passed — no leaks detected. Passed Scenarios (4)
|
WebGPUHardwareTexture.format defaulted to RGBA8Unorm and wrapWebGPUTexture never overwrote it. The RTT color-attachment view and pipeline color target are built from hardwareTexture.format (_setColorFormat / bindFramebuffer), so an external texture created with any other format (e.g. bgra8unorm or an *-srgb variant, as returned by XRGPUBinding.getPreferredColorFormat()) got a mismatched view and failed to render (black in WebGPU-XR). Set both format and originalFormat from the wrapped GPUTexture's own format. General correctness for any external-texture wrap; rgba8unorm callers are unchanged. Orthogonal to the depth-format fix (that sets InternalTexture.format). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…r (GAP 2) WebGPU/Quest projection layers hand out a texture ARRAY with one layer per eye (depthOrArrayLayers=2, baseArrayLayer 0=left / 1=right). The render path hard-coded baseArrayLayer=0, so the right eye's layer was never written and both views composited into layer 0 -> black/broken stereo despite correct per-eye resolution and IN_XR. - RenderTargetTexture: add @internal _bindFrameBufferLayer (default 0) that _bindFrameBuffer defaults its layer arg from. WebGL2 + non-XR paths are byte-identical (field stays 0; explicit-layer callers unchanged). - WebGPU XR provider: set _bindFrameBufferLayer per eye from the authoritative subImage.getViewDescriptor().baseArrayLayer. Depth attachment inherits the same layer via the existing baseArrayLayer=layer wiring. - Add unit test asserting left->layer 0, right->layer 1. Zero net public API (@internal). Confirmed on Quest by RaananW: readout baseArrayLayer 0/1 per eye. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
🟢 Memory Leak Test Results4 passed, 0 leaked out of 4 scenarios 🟢 All memory leak tests passed — no leaks detected. Passed Scenarios (4)
|
|
Visualization tests for WebGPU |
|
WebGL2 visualization test reporter: |
⚡ Performance Test Results🟢 All performance tests passed — no regressions detected. |
Phase 2 — WebGPU XR projection layer + render target provider
Tracking issue: #18640 · Epic: #18635 · Follows Phase 0 (#18645) and Phase 1 (#18650).
Goal
Implement the core WebGPU XR rendering path: render the stereo scene into an
XRProjectionLayer's per‑viewGPUTextures viaXRGPUBinding, so a WebGPU XR session on Quest reachesIN_XRand actually renders. Phase 1 enters the session but stays blank because no layer is attached and no RTT provider produces textures — this PR closes that gap.WebGL2 XR remains byte‑for‑byte unchanged. Everything new is gated behind
isWebGPU/ binding‑type branches, is@internal, and is kept out of the public barrels. Zero net public‑API additions.What's in this PR (two commits for reviewability, one mergeable unit)
Commit 1 — WebGPU RTT providers (additive, inert until commit 2):
XR/webXRWebGPURenderTargetTextureProvider.ts— abstract@internalbase extending the API‑agnosticWebXRLayerRenderTargetTextureProvider. Wraps per‑view color/depthGPUTextures viaWebGPUEngine.wrapWebGPUTexture, sets the correct Babylon depth format on the wrapped depth texture, repoints per‑frame viaupdateWrappedWebGPUTexture(rebuilds only on size change), and builds RTTs via_createRenderTargetTextureInternal.XR/features/Layers/WebXRWebGPUCompositionLayer.ts— WebGPU composition provider + wrapper mirroring the WebGL provider overXRGPUSubImage(dimensions fromcolorTexture.width/height), size‑keyed cache with repoint fast‑path and viewport normalization.XR/features/Layers/WebXRWebGPUProjectionLayer.ts— WebGPU projection wrapper/provider usinggetViewSubImage, plus the defaultXRGPUProjectionLayerInitfactory.test/unit/XR/webXRWebGPUProjectionLayer.test.ts).Commit 2 — WebXRLayers activation:
XR/features/WebXRLayers.pure.ts— branchesattach()/isCompatible()onengine.isWebGPUto create anXRGPUBindingprojection layer (via the Phase 1 graphics binding) and instantiate the WebGPU wrapper. The WebGL branch is only bypassed whenisWebGPUand anXRGPUBindingis present.Key implementation notes / fork resolutions
wrapWebGPUTexturedoesn't set the BabylonInternalTexture.format; the WebGPU depth attachment path reads it. The provider sets the mapped Babylon depth format on the wrapped depth texture.wrapWebGPUTextureis left untouched (minimal general‑purpose primitive).updateWrappedWebGPUTexturefast path preserves RTT/InternalTexture identity (held byCamera.outputRenderTarget) and bumpsuniqueIdto invalidate bind‑group caches; full re‑wrap only on size change (the update guard requires matching dims).Camera.outputRenderTarget → bindFramebuffer → _startRenderTargetRenderPasspath. The swapchain (getCurrentTexture) is only used when no render target is bound, so it is untouched during XR.samples=1for Phase 2. Babylon‑managed MSAA resolve into the external single‑sample color texture already works via the existinggetMSAATexture/resolveTargetpath and needs only RTTsamples>1(no engine work). Deliberately deferred to isolate the first hardware validation — enabling it will be a small fast-follow after RaananW confirms the base stereo render works on Quest.getViewSubImage);multiview=falsefor WebGPU. Multiview is deferred (Phase 4+).Scope boundaries
[0,1]range — NOT touched here.Testing
tsc -b(core): clean.npm run format:check,npm run lint:check(incl. 16 tree‑shaking checks + side‑effects‑sync): all green.